use std::path::{Path, PathBuf};
use std::fs;

use agent::generic::ProviderConfig;
use automation::spider_utils::CrawlResult;
use common::compose::{make_empty_ctx, CallBox, Composer, FError, ListHandler};
use log::{info, debug};
use markdown::create_html_to_markdown;

use crate::{make_text_splitter_callbox, TextSplitterConfig};

/// Markdown 转换结果
#[derive(Debug, Clone)]
pub struct MarkdownResult {
    /// 原始 URL
    pub url: String,
    /// Markdown 内容
    pub content: String,
    /// 语言代码
    pub lang: String,
    /// 输出文件路径
    pub output_path: Option<PathBuf>,
}

/// Markdown 转换配置
#[derive(Debug, Clone)]
pub struct MarkdownConfig {
    /// 输出目录
    pub output_dir: String,
    /// HTML 到 Markdown 转换的提供商配置
    pub html_to_md_provider_config: ProviderConfig,
    /// HTML 到 Markdown 转换的模型
    pub html_to_md_model: String,
    /// 文本分割配置
    pub text_splitter_config: TextSplitterConfig,
    /// 并行处理数量
    pub parallel_num: usize,
}

impl Default for MarkdownConfig {
    fn default() -> Self {
        Self {
            output_dir: "output/md".to_string(),
            html_to_md_provider_config: ProviderConfig::OpenAI {
                api_key: "".to_string(),
                base_url: None,
                temperature: 0.3,
                max_tokens: None,
            },
            html_to_md_model: "".to_string(),
            text_splitter_config: TextSplitterConfig::default(),
            parallel_num: 4,
        }
    }
}

// 使用公共模块中的语言检测功能
pub use crate::common::lang::make_check_lang_callbox;

/// 创建一个分割HTML的CallBox
///
/// # 参数
///
/// * `config` - 文本分割配置
///
/// # 返回值
///
/// 返回一个CallBox，该CallBox接收HTML内容，返回分割后的HTML块列表
fn make_split_html_callbox(config: TextSplitterConfig) -> CallBox<'static, String, Vec<String>> {
    Composer::from_fn(move |html: String| {
        let config = config.clone();
        async move {
            let text_splitter = make_text_splitter_callbox(config);
            let chunks = text_splitter.run(html, make_empty_ctx()).await?;
            debug!("HTML 分割为 {} 个块", chunks.len());
            Ok(chunks)
        }
    })
}

/// 创建一个将HTML块转换为Markdown块的CallBox
///
/// # 参数
///
/// * `provider_config` - 提供商配置
/// * `model` - 模型名称
/// * `parallel_num` - 并行处理数量
///
/// # 返回值
///
/// 返回一个CallBox，该CallBox接收HTML块列表，返回Markdown块列表
fn make_html_to_md_chunks_callbox(
    provider_config: ProviderConfig,
    model: String,
    parallel_num: usize
) -> CallBox<'static, Vec<String>, Vec<String>> {
    let html_to_md = create_html_to_markdown(
        Some((provider_config, &model)),
    );

    ListHandler::new(parallel_num, html_to_md)
}

/// 创建一个合并Markdown块的CallBox
///
/// # 返回值
///
/// 返回一个CallBox，该CallBox接收Markdown块列表，返回合并后的Markdown
fn make_combine_md_chunks_callbox() -> CallBox<'static, Vec<String>, String> {
    Composer::from_fn(|chunks: Vec<String>| {
        async move {
            let combined = chunks.join("\n");
            debug!("Markdown 合并完成，大小: {} 字节", combined.len());
            Ok(combined)
        }
    })
}



/// 创建一个将 HTML 转换为 Markdown 的处理流程 CallBox
fn make_html_to_md_pipeline_callbox(
    text_splitter_config: TextSplitterConfig,
    provider_config: ProviderConfig,
    model: String,
    parallel_num: usize
) -> CallBox<'static, String, String> {
    // 1. 分割 HTML
    let split_html = make_split_html_callbox(text_splitter_config);

    // 2. 将 HTML 块转换为 Markdown 块
    let html_to_md_chunks = make_html_to_md_chunks_callbox(
        provider_config,
        model,
        parallel_num
    );

    // 3. 合并 Markdown 块
    let combine_md_chunks = make_combine_md_chunks_callbox();

    // 组合这三个步骤
    Composer::compose3(
        split_html,
        html_to_md_chunks,
        combine_md_chunks
    )
}



/// 创建一个处理单个CrawlResult的CallBox
///
/// # 参数
///
/// * `config` - Markdown转换配置
///
/// # 返回值
///
/// 返回一个CallBox，该CallBox接收CrawlResult，返回MarkdownResult
fn make_process_single_callbox(config: MarkdownConfig) -> CallBox<'static, CrawlResult, MarkdownResult> {
    // 记录处理的URL
    debug!("创建处理单个 CrawlResult 的 CallBox");

    // 2. HTML 转换为 Markdown 的处理流程
    let html_to_md_pipeline = make_html_to_md_pipeline_callbox(
        config.text_splitter_config.clone(),
        config.html_to_md_provider_config.clone(),
        config.html_to_md_model.clone(),
        config.parallel_num
    );

    // 3. 检测语言
    let check_lang = make_check_lang_callbox(config.html_to_md_provider_config.clone());

    // 4. 创建 MarkdownResult
    let create_md_result = Composer::from_fn(move |result: CrawlResult| {
        let html_to_md_pipeline = html_to_md_pipeline.clone();
        let check_lang = check_lang.clone();

        async move {
            // 处理 HTML 内容
            let markdown = html_to_md_pipeline.run(result.content.clone(), make_empty_ctx()).await?;

            // 检测语言
            let lang = check_lang.run(markdown.clone(), make_empty_ctx()).await?;
            debug!("检测到语言: {}", lang);

            // 创建 MarkdownResult
            let md_result = MarkdownResult {
                url: result.url.clone(),
                content: markdown,
                lang,
                output_path: None,
            };

            Ok(md_result)
        }
    });

    // 5. 提取文件名并保存
    let save_with_filename = Composer::from_fn(move |md_result: MarkdownResult| {
        let output_dir = config.output_dir.clone();

        async move {
            // 从 URL 中提取文件名
            let parsed_url = url::Url::parse(&md_result.url)
                .map_err(|e| FError::Generic(Box::new(e)))?;
            let url_path = parsed_url.path();

            // 获取文件名
            let file_name = if url_path.ends_with('/') || url_path == "/" {
                "index.md".to_string()
            } else {
                let path = Path::new(url_path);
                let stem = path.file_stem().map_or("index", |s| s.to_str().unwrap_or("index"));
                format!("{}.md", stem)
            };

            // 创建语言子目录
            let lang_dir = Path::new(&output_dir).join(&md_result.lang);

            // 确保输出目录存在
            if !lang_dir.exists() {
                fs::create_dir_all(&lang_dir).map_err(|e| FError::Generic(Box::new(e)))?;
                debug!("创建输出目录: {}", lang_dir.display());
            }

            // 创建输出文件路径
            let output_path = lang_dir.join(&file_name);

            // 保存文件
            fs::write(&output_path, &md_result.content).map_err(|e| FError::Generic(Box::new(e)))?;
            debug!("保存文件: {}", output_path.display());

            // 返回带有输出路径的结果
            let mut result = md_result;
            result.output_path = Some(output_path);

            Ok(result)
        }
    });

    // 6. 组合所有步骤
    Composer::compose(
        create_md_result,
        save_with_filename
    )
}

/// 创建一个 Markdown 转换器 CallBox
///
/// 该 CallBox 接收一个 CrawlResult 列表，将 HTML 转换为 Markdown，并返回 MarkdownResult 列表
///
/// # 参数
///
/// * `config` - Markdown 转换配置
///
/// # 返回值
///
/// 返回一个 CallBox，该 CallBox 接收一个 CrawlResult 列表，返回 MarkdownResult 列表
pub fn make_markdown_callbox(config: MarkdownConfig) -> CallBox<'static, Vec<CrawlResult>, Vec<MarkdownResult>> {
    info!("创建 Markdown 转换器");
    debug!("使用串行处理模式");
    debug!("输出目录: {}", config.output_dir);

    // 处理单个 CrawlResult 的 CallBox
    let process_single = make_process_single_callbox(config.clone());

    // 使用 make_seq_handler_callbox 串行处理多个 CrawlResult
    Composer::make_seq_handler_callbox(process_single)
}
